package com.touch.core;
import java.util.*;
import java.net.*;

import com.touch.core.net.NetListener;
/**
 * The most basic Net Object. It covers all of the required methods in Net Objects and is extended by both UDP and TCP clients.
 * @author Homicidal Monkey
 * @version 10.1.13
 * @since r463
 * @see com/touch/Licence
 */
public abstract class AbstractNetObject extends NetConstants implements Runnable 
{
	//index of this object
	private int index;
	//total net objects
	private static int INDEX;
	//maximum size of a buffer. This value can be changed
	public static int BUFFERSIZE = 256;	
	//list of net listeners
	protected final Vector<NetListener> listeners = new Vector<NetListener>();
	//the thread for the net object
	protected Thread netProcessThread;
	//value that determines if the net object is still running
	protected boolean isAlive = false;
	//remote address of the socket.
	protected InetSocketAddress remoteAddress;
	/**
	 * Returns the address that the socket is bound to.
	 * @return InetSocketAddress bound to the socket.
	 * @since r463
	 */
	public InetSocketAddress getAddress()
	{
		//return the address.
		return this.remoteAddress;
	}		
	/**
	 * Creates a new Net Object.
	 * @since r463
	 */
	public AbstractNetObject()
	{		
		//set the index
		this.index = INDEX;
		//create the new thread for the object
		this.netProcessThread = new Thread(this);
		//set the name of the thread
		this.netProcessThread.setName("NetObject " + Integer.toHexString(index));
		//set the object for being alive.
		this.isAlive = true;
		//increase the index for net objects
		INDEX++;
	}	
	/**
	 * Returns the index of the NetObject. Each NetObject has its own unique index value.
	 * @return Unique index of the NetObject
	 * @since r466
	 */
	public int getIndex()
	{
		//return the NetObject's index.
		return this.index;
	}
	/**
	 * Stops the Socket's thread.
	 * @since r463
	 */
	public void stop()
	{
		//stop the thread.
		this.isAlive = false;
	}
	/**
	 * Starts the Socket's thread.
	 * @since r463
	 */
	public void start()
	{
		//start the thread.
		this.getThread().start();
	}
	/**
	 * Removes all listeners from the NetObject
	 * @since r463
	 */
	public void removeAllNetListeners()
	{
		//remove all the elements of the vector of listeners
		this.listeners.removeAllElements();
	}
	/**
	 * Removes a single NetListener from the list of listeners
	 * @param listener NetListener to remove
	 * @since r467
	 */
	public void removeNetListener(final NetListener listener)
	{
		//removes a single listener from the list of listeners by address point
		this.listeners.remove(listener);
	}
	/**
	 * Adds a new NetListener to the NetObject. NetListeners are fired when new messages are received.	
	 * @param listener New NetListener to add to the NetObject.
	 * @since r463
	 */
	public void addNetListener(final NetListener listener)
	{
		//add the new NetListener
		this.listeners.add(listener);
	}
	/**
	 * Fires all the NetListeners.
	 * @param host Any object that is used to recognize where the message came from. This may differ between TCP and UDP Objects.
	 * @param message The incoming message.
	 * @since r463
	 */
	public void invokeNetListener(final Object host, final byte[] message)
	{
		//fire each net listener. Note: this goes in order from when it was added.
		for(NetListener listener: listeners)
			listener.messageReceived(host, message);
	}
	/**
	 * Returns the thread associated with the NetObject.
	 * @deprecated It is not recommended to use this method since NetObject.start() and NetObject.stop() have error handling.	
	 * @return Thread associated with the NetObject.
	 * @since r463
	 */
	public Thread getThread()
	{
		//return the thread.
		return this.netProcessThread;
	}
	/**
	 * Determines if the computer can be reached
	 * @param host IPv4/IPv6 address to connect
	 * @return Boolean value specifying if the host is reachable.
	 * @since r463
	 */
	public static boolean canReach(final String host)
	{
		try
		{
			//attempts to retrieve an address from the String given
			final InetAddress address = InetAddress.getByName(host);
			//allow 200ms to connect
			if(address.isReachable(200))
				//return true since it was able to connect
				return true;
			else
				//return false since it was not able to connect
				return false;
		}
		catch(final Exception e)
		{		
			//an error occurred so conclude that the address is unreachable
			return false;
		}
	}
	/**
	 * Closes the NetObject.
	 * @since r463
	 */
	public abstract void close();	
}
